跳到主要内容

MySQL 学习(3) CheckPoint 技术

CheckPoint 技术是什么?

先来看下 CheckPoint 之前有啥问题?

缓冲池的设计目的为了协调 CPU 速度与磁盘速度的鸿沟。因此页的操作首先都是在缓冲池中完成的。如果一条 DML 语句,如 Update 或 Delete 改变了页中的记录,那么此时页是脏的,即缓冲池中的页的版本要比磁盘的新。数据库需要将新版本的页从缓冲池刷新到磁盘。

倘若每次一个页发生变化,就将新页的版本刷新到磁盘,那么这个开销是非常大的。若热点数据集中在某几个页中,那么数据库的性能将变得非常差。同时,如果在从缓冲池将页的新版本刷新到磁盘时发生了宕机,那么数据就不能恢复了。

为了避免发生数据丢失的问题,当前事务数据库系统普遍都采用了 Write Ahead Log 策略,即当事务提交时,先写重做日志,再修改页。当由于发生宕机而导致数据丢失时,通过重做日志来完成数据的恢复。这也是事务 ACID 中 D (Durability 持久性)的要求。

思考下面的场景,如果重做日志可以无限地增大,同时缓冲池也足够大,能够缓冲所有数据库的数据,那么是不需要将缓冲池中页的新版本刷新回磁盘。

因为当发生宕机时,完全可以通过重做日志来恢复整个数据库系统中的数据到宕机发生的时刻。但是这需要两个前提条件:

  • 缓冲池可以缓存数据库中所有的数据;
  • 重做日志可以无限增大。

对于第一个前提条件,有经验的用户都知道,当数据库刚开始创建时,表中没有任何数据。缓冲池的确可以缓存所有的数据库文件。然而随着市场的推广,用户的增加,产品越来越受到关注,使用量也越来越大。这时负责后台存储的数据库的容量必定会不断增大。当前 3TB 的 MySQL 数据库已并不少见,但是 3TB 的内存却非常少见。因此第一个假设对于生产环境应用中的数据库是很难得到保证的。

再来看第二个前提条件:重做日志可以无限增大。也许是可以的,但是这对成本的要求太高,同时不便于运维。DBA 或 SA 不能知道什么时候重做日志是否已经接近于磁盘可使用空间的阈值,并且要让存储设备支持可动态扩展也是很麻烦的

就算上面的两个条件都能满足,但是如果运行了几个月甚至几年,这时宕机,恢复的代价会非常大(重新应用重做日志的时间会非常久)

因此 Checkpoint(检查点)技术的目的是解决以下几个问题:

  • 缩短数据库的恢复时间;
  • 缓冲池不够用时,将脏页刷新到磁盘;
  • 重做日志不可用时,刷新脏页。

当数据库发生宕机时,数据库不需要重做所有的日志,因为 Checkpoint 之前的页都已经刷新回磁盘。故数据库只需对 Checkpoint 后的重做日志进行恢复。这样就大大缩短了恢复的时间。

此外,当缓冲池不够用时,根据 LRU 算法会溢出最近最少使用的页,若此页为脏页,那么需要强制执行 Checkpoint,将脏页也就是页的新版本刷回磁盘。

重做日志出现不可用的情况是因为当前事务数据库系统对重做日志的设计都是循环使用的,并不是让其无限增大的,这从成本及管理上都是比较困难的。

重做日志可以被重用的部分是指这些重做日志已经不再需要,即当数据库发生宕机时,数据库恢复操作不需要这部分的重做日志,因此这部分就可以被覆盖重用。若此时重做日志还需要使用,那么必须强制产生 Checkpoint,将缓冲池中的页至少刷新到当前重做日志的位置。

对于 InnoDB 存储引擎而言,其是通过 LSN(Log Sequence Number)来标记版本的。而 LSN 是 8字节的数字,其单位是字节。每个页有 LSN,重做日志中也有 LSN,Checkpoint 也有 LSN。可以通过命令来观察:

SHOW ENGINE INNODB STATUS 

CheckPoint 的分类

在 InnoDB 存储引擎中,Checkpoint 发生的时间、条件及脏页的选择等都非常复杂。

而 Checkpoint 所做的事情无外乎是将缓冲池中的脏页刷回到磁盘。不同之处在于每次刷新多少页到磁盘,每次从哪里取脏页,以及什么时间触发 Checkpoint。在 InnoDB 存储引擎内部,有两种 Checkpoint,分别为:

  • Sharp Checkpoint:在关闭数据库的时候,将 buffer pool 中的脏页全部刷新到磁盘中。
  • Fuzzy Checkpoint:数据库正常运行时,在不同的时机,将部分脏页写入磁盘,进刷新部分脏页到磁盘,也是为了避免一次刷新全部的脏页造成的性能问题。

Sharp Checkpoint 发生在数据库关闭时将所有的脏页都刷新回磁盘,这是默认的工作方式,即参数 innodb_fast_shutdown = 1

但是若数据库在运行时也使用 Sharp Checkpoint,那么数据库的可用性就会受到很大的影响。故在 InnoDB 存储引擎内部使用 Fuzzy Checkpoint 进行页的刷新,即只刷新一部分脏页,而不是刷新所有的脏页回磁盘。

在 InnoDB 存储引擎中可能发生如下几种情况的 Fuzzy Checkpoint:

  • Master Thread Checkpoint
  • FLUSH LRU LIST Checkpoint
  • Async / Sync Flush Checkpoint
  • Dirty Page too much Checkpoint

CheckPoint 发生的时机

CheckPoint 都是将 buffer pool 中的脏页刷新到磁盘,但是在不同的情况下,CheckPoint 会被以不同的方式触发,同时写入到磁盘的脏页的数量也不同。

Master Thread checkpoint

对于 Master Thread 中发生的 Checkpoint,差不多以每秒或每十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘。这个过程是异步的,即此时 InnoDB 存储引擎可以进行其他的操作,用户查询线程不会阻塞。

FLUSH LRU LIST Checkpoint

而 FLUSH_LRU_LIST Checkpoint 是因为 InnoDB 存储引擎需要保证 LRU 列表中需要有差不多 100 个空闲页可供使用。在 InnoDB 1.1.x 版本之前,需要检查 LRU 列表中是否有足够的可用空间操作发生在用户查询线程中,显然这会阻塞用户的查询操作。

倘若没有 100 个可用空闲页,那么 InnoDB 存储引擎会将 LRU 列表尾端的页移除。如果这些页中有脏页,那么需要进行 Checkpoint,而这些页是来自 LRU 列表的,因此称为 FLUSH_LRU_LIST Checkpoint

空闲页的数量由 innodb_lru_scan_depth 参数表来控制的。

show variables like 'innodb_lru_scan_depth';

Async/Sync Flush checkpoint

Async/Sync Flush checkpoint 是在单独的 page cleaner 线程中执行的。

Async/Sync Flush checkpoint 发生在重做日志不可用的时候,将 buffer pool 中的一部分脏页刷新到磁盘中,在脏页写入磁盘之后,事物对应的重做日志也就可以释放了。

关于 redo_log 文件的的大小,可以通过 innodb_log_file_size 来配置。

show variables like 'innodb_log_file_size';

对于是执行 Async Flush checkpoint 还是 Sync Flush checkpoint,由 checkpoint_age 以及 async_water_mark 和 sync_water_mark 来决定。

定义:

checkpoint_age=redo_lsncheckpoint_lsncheckpoint\_age = redo\_lsn - checkpoint\_lsn

也即 checkpoint_age 等于最新的 lsn 减去已经刷新到磁盘的 lsn 的值

async_water_mark=75%innodb_log_file_sizeasync\_water\_mark = 75\% * innodb\_log\_file\_size

sync_water_mark=90%innodb_log_file_sizesync\_water\_mark = 90\% * innodb\_log\_file\_size

1、当 checkpoint_age < sync_water_mark 的时候,无需执行 Flush checkpoint。也就说,redo log 剩余空间超过 25% 的时候,无需执行 Async/Sync Flush checkpoint。

2、当 async_water_mark < checkpoint_age < sync_water_mark 的时候,执行 Async Flush checkpoint,也就说,redo log 剩余空间不足 25%,但是大于 10% 的时候,执行 Async Flush checkpoint,刷新到满足条件 1

3、当 checkpoint_age > sync_water_mark 的时候,执行 Sync Flush checkpoint。也就说,redo log 剩余空间不足 10% 的时候,执行 Sync Flush checkpoint,刷新到满足条件 1。

在 mysql 5.6之后,不管是 Async Flush checkpoint 还是 Sync Flush checkpoint,都不会阻塞用户的查询进程。

因为 innodb_log_file_size 定义的是一个相对较大的值,正常情况下,是由前面两种 checkpoint 刷新脏页到磁盘,在前面两种 checkpoint 刷新脏页到磁盘之后,脏页对应的 redo log 空间随即释放,一般不会发生 Sync Flush checkpoint。同时也要意识到,为了避免频繁的发生 Async/Sync Flush checkpoint,也应该将 innodb_log_file_size 配置的相对较大一些。

Dirty Page too much Checkpoint

Dirty Page too much Checkpoint 是在 Master Thread 线程中每秒一次的频率实现的。Dirty Page too much 意味着 buffer pool 中的脏页过多,执行 checkpoint 脏页刷入磁盘,保证 buffer pool 中有足够的可用页面。

Dirty Page 由 innodb_max_dirty_pages_pct 配置,innodb_max_dirty_pages_pct 的默认值在 innodb 1.0 之前是 90%,之后是 75%。

show variables like 'innodb_max_dirty_pages_pct';

Checkpoint 总结

MySQL 数据库(当然其他关系数据也有类似的机制),为了提高事物操作的效率,在事物提交之后并不会立即将修改后的数据写入磁盘,而是通过日志先行(write log ahead)的方式保证事物的持久性。

对于将事物修改的数据页面,也即脏页,通过异步的方式刷新到磁盘中,Checkpoint 正是实现这种异步刷新脏页到磁盘的实施者。

不同的情况下,会发生不同的 checkpoint,将不同数量的脏页刷新到磁盘,从而到达管理内存(第 1,2,4 种 checkpoint)和 redo log 可用空间(第3种checkpoint)的目的。

References